package Server;
import Conn.Conn;
import DES.DES;
import RSA.RSA;
import Ticket.Ticket;
import Message.Message;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Server {
    ServerSocket server = null;
    int port=1001;
    String ID="00002";

    public Ticket toTicket(String s)
    {
        String []a=s.split("-");
        Ticket m=new Ticket(a[Ticket.pos.ID_src.ordinal()],
                a[Ticket.pos.ID_dst.ordinal()],
                Integer.parseInt(a[Ticket.pos.lifetime.ordinal()]),
                a[Ticket.pos.ADC.ordinal()],
                a[Ticket.pos.key.ordinal()]
        );
        String time=a[Ticket.pos.TS.ordinal()];
        int day=Integer.parseInt(time.substring(0,2));
        int hour=Integer.parseInt(time.substring(2,4));
        int minute=Integer.parseInt(time.substring(4,6));
        Calendar c=Calendar.getInstance();
        c.set(Calendar.HOUR,hour);
        c.set(Calendar.MINUTE,minute);
        c.set(Calendar.DATE,day);
        m.setTS(c);
        return m;
    }

    public Message toMessage(String s)
    {
        String []a=s.split("\\|");
        Message m=new Message(a[Message.pos.tag.ordinal()],
                a[Message.pos.ID_src.ordinal()],
                a[Message.pos.ID_dst.ordinal()],
                Integer.parseInt(a[Message.pos.lifetime.ordinal()]),
                a[Message.pos.content.ordinal()],
                a[Message.pos.redu.ordinal()]
        );
        String time=a[Message.pos.TS.ordinal()];
        int day=Integer.parseInt(time.substring(0,2));
        int hour=Integer.parseInt(time.substring(2,4));
        int minute=Integer.parseInt(time.substring(4,6));
        Calendar c=Calendar.getInstance();
        c.set(Calendar.HOUR,hour);
        c.set(Calendar.MINUTE,minute);
        c.set(Calendar.DATE,day);
        m.setTS(c);
        return m;
    }

    public void Start()
    {

        try {
            server = new ServerSocket(port);
            System.out.println("正在监听端口"+port);
        } catch (IOException e) {
            e.printStackTrace();
        }
        ExecutorService pool = Executors.newFixedThreadPool(5);
        class myRunnable implements Runnable {
            DataOutputStream writer = null;
            DataInputStream reader = null;

            Socket client;
            static int numb=0;
            boolean isCorrect(String s)
            {
                //解密s
                return true;
            }
            public void send(String s)
            {

                try {
                    writer.writeUTF(s);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            public String recv()
            {
                try {
                    return reader.readUTF();
                } catch (IOException e) {
                    return "";
                }
            }
            void send(String s,String key)
            {
//                Calendar cal=Calendar.getInstance();
//                key=key+ cal.getTime();
                try {
                    send(DES.encrypt(s,DES.getSecretKey(key)));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            String recv(String key)
            {
//                Calendar cal=Calendar.getInstance();
//                key=key+ cal.getTime();
                String s=recv();
                try {
                    s=DES.decrypt(s,DES.getSecretKey(key));
                    return s;
                } catch (Exception e) {
                    return null;
                }
            }
            myRunnable(Socket nclient)
            {
                this.client = nclient;
                numb++;
                try {
                    DataOutputStream writer = new DataOutputStream(this.client.getOutputStream());
                    DataInputStream reader = new DataInputStream(this.client.getInputStream());
                    this.writer=writer;
                    this.reader=reader;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            public Message process(Message m)
            {
                String key_td="";
                String key_dv="";
                String key_vd="";
                Ticket au;
                Message newm=null;
                Ticket t=null;
                String sql;
                ResultSet rs;
                Conn c=new Conn();
                c.getConnection();
                Message mnull=new Message("00000",ID,m.getID_src(),5,"error"," ");
                switch (Integer.parseInt(m.getTag()))
                {
                    case 5:
                        if(Objects.equals(m.getID_dst(), new Server().ID))
                        {
                            try {
                                t=toTicket(DES.decrypt(m.getContent(),DES.getSecretKey("00002")));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            key_td=t.getKey();
                            sql="select d_key_dv,d_key_vd from device where d_ID='%s';".formatted(m.getID_src());
                            rs=c.execute(sql);
                            try {
                                rs.next();
                                key_dv=rs.getString(2);
                                key_vd=rs.getString(1);
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                            try {
                                RSA.decrypt(m.getRedu(),key_td);
                            } catch (Exception e) {
                                e.printStackTrace();
                                return mnull;

                            }
                            if(!Objects.equals(client.getInetAddress().getHostAddress(), t.getADC()))
                            {
                                return mnull;
                            }
                            t=new Ticket("00002",m.getID_src(),5,"localhost",key_vd);
                            try {
                                newm=new Message("00006",new Server().ID,m.getID_src(),120,DES.encrypt(t.toStr(),DES.getSecretKey("00003")),key_dv);
                            } catch (Exception e)    {
                                e.printStackTrace();
                            }
                        }

                        break;
                    case 2:
                        break;
                    case 11:
                        if(Objects.equals(m.getID_dst(), new Server().ID))
                        {
                            try {
                                t=toTicket(DES.decrypt(m.getContent(),DES.getSecretKey("00002")));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            key_td=t.getKey();
                            sql="select d_key_dv,d_key_vd from device where d_ID='%s';".formatted(m.getID_src());
                            rs=c.execute(sql);
                            try {
                                rs.next();
                                key_dv=rs.getString(2);
                                key_vd=rs.getString(1);
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                            try {
                                RSA.decrypt(m.getRedu(),key_td);
                            } catch (Exception e) {
                                e.printStackTrace();
                                return mnull;

                            }
                            if(!Objects.equals(client.getInetAddress().getHostAddress(), t.getADC()))
                            {
                                return mnull;
                            }
                            t=new Ticket("00002",m.getID_src(),5,"localhost",key_vd);
                            try {
                                newm=new Message("00012",new Server().ID,m.getID_src(),120,DES.encrypt(t.toStr(),DES.getSecretKey("00003")),key_dv);
                            } catch (Exception e)    {
                                e.printStackTrace();
                            }
                        }

                }
                return newm;
            }
            @Override
            public void run()
            {
                Message m;
                Ticket t;
                System.out.println(Thread.currentThread().getName());
                System.out.println("serve for " + this.client.getInetAddress()+",linked with "+numb+" clinets");
                try
                {
                    while(true)
                    {

                        String s=recv();
                        if( s.equals(""))
                        {
                            break;
                        }
                        m=toMessage(s);
                        System.out.println("recv from client: "+m.toStr());
//                        System.out.println(s);
                        String k=" ";
                        m=process(m);
                        send(m.toStr(),m.getID_dst());
                        System.out.println("send to client: "+m.toStr());
                    }

                    client.close();
                    numb--;
                    System.out.println("client close\n");
//                    client.shutdownInput();
//                    client.shutdownOutput();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                //client.close();
            }
        } //重写Runnable接口
        while(true){
            try {
                Socket new_client= server.accept();
                pool.execute(new myRunnable(new_client));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
